package pe.com.casadesign.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;

import pe.com.casadesign.dao.ClienteDAO;
import pe.com.casadesign.domain.Cliente;
import pe.com.casadesign.domain.ContactoCliente;
import pe.com.casadesign.domain.ListaCliente;
import pe.com.casadesign.domain.ListaContactoCliente;
import pe.com.casadesign.domain.Distrito;

/**
 * Implementacion de ClienteDAO.
 * 
 * @author Pedro Vera
 * 
 */
@Repository
public class ClienteDAOImpl implements ClienteDAO {
	private static final Logger logger = LoggerFactory.getLogger(ClienteDAOImpl.class);
	
	// Nombre Procedimientos Almacenados
	private static String SP_LISTAR_CLIENTE = "SP_LISTAR_CLIENTE";
	private static String SP_OBTENER_CLIENTE = "SP_OBTENER_CLIENTE";
	private static String SP_OBTENER_CLIENTE_PORDOC = "SP_OBTENER_CLIENTE_PORDOC";
	private static String SP_REGISTRAR_CLIENTE = "SP_REGISTRAR_CLIENTE";
	private static String SP_MODIFICAR_CLIENTE = "SP_MODIFICAR_CLIENTE";
	private static String SP_ELIMINAR_CLIENTE = "SP_ELIMINAR_CLIENTE";
	
	private static String SP_REGISTRAR_CONTACTO_CLIENTE = "SP_REGISTRAR_CONTACTO_CLIENTE";
	private static String SP_LIMPIAR_CONTACTO_CLIENTE = "SP_LIMPIAR_CONTACTO_CLIENTE";
		
	// Parametros de Entrada
	private static String PARAM_CODCLIENTE = "p_codcliente";
	private static String PARAM_RAZONSOCIAL = "p_razonsocial";
	private static String PARAM_RUC = "p_ruc";
	private static String PARAM_DIRECCION = "p_direccion";	
	private static String PARAM_CODDISTRITO = "p_coddistrito";
	private static String PARAM_CODPROVINCIA = "p_codprovincia";
	private static String PARAM_CODREGION = "p_codregion";
	private static String PARAM_TIPODOCUMENTO = "p_tipodocumento";
	private static String PARAM_NRODOCUMENTO = "p_nrodocumento";
	private static String PARAM_ESTADO = "p_estado";
	
	private static String PARAM_CONTACTO_NOMBRESYAPELLIDOS = "p_nombresyapellidos";
	private static String PARAM_CONTACTO_EMAIL = "p_email";
	private static String PARAM_CONTACTO_INDENVIOEMAIL = "p_indenvioemail";
	private static String PARAM_CONTACTO_TELEFONO = "p_telefono";
	private static String PARAM_CONTACTO_RELACION = "p_relacion";
	
	// Parametros de Salida
	private static String CODCLIENTE = "CODCLIENTE";
	private static String RAZONSOCIAL = "RAZONSOCIAL";
	private static String RUC = "RUC";
	private static String DIRECCION = "DIRECCION";
	private static String CODDISTRITO = "CODDISTRITO";
	private static String CODPROVINCIA = "CODPROVINCIA";
	private static String CODREGION = "CODREGION";
	private static String DISTRITO_DESCRIPCION = "DESCRIPCION";
	private static String TIPODOCUMENTO = "TIPODOCUMENTO";
	private static String NRODOCUMENTO = "NRODOCUMENTO";
	private static String ESTADO = "ESTADO";
	private static String FECHAREGISTRO = "FECHAREGISTRO";
	
	private static String CONTACTO_NOMBRESYAPELLIDOS = "NOMBRESYAPELLIDOS";
	private static String CONTACTO_EMAIL = "EMAIL";
	private static String CONTACTO_INDENVIOEMAIL = "INDENVIOEMAIL";
	private static String CONTACTO_TELEFONO = "TELEFONO";
	private static String CONTACTO_RELACION = "RELACION";
	
	private static String LISTADO = "LISTADO";
	private static String LISTADO_CONTACTOS = "LISTADO_CONTACTOS";
	
	private static String TOTAL_FILAS_AFECTADAS = "#update-count-1";
	
	private JdbcTemplate jdbcTemplate;

	@Autowired
    public void setDataSource(DataSource dataSource)
    {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
	
	@Override
	public ListaCliente listarCliente(Cliente cliente) {
		logger.info("listarCliente");

		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_LISTAR_CLIENTE).
	    						  returningResultSet(LISTADO, new RowMapper<Cliente>(){
									@Override
									public Cliente mapRow(final ResultSet rs, final int numRow) throws SQLException
								    {
								        final Cliente cliente = new Cliente();
								        cliente.setCodCliente(rs.getInt(CODCLIENTE));
								        cliente.setRazonSocial(rs.getString(RAZONSOCIAL));
								        cliente.setRuc(rs.getString(RUC));
								        cliente.setDireccion(rs.getString(DIRECCION));
								        cliente.setTipoDocumento(rs.getString(TIPODOCUMENTO));
								        cliente.setNroDocumento(rs.getString(NRODOCUMENTO));
								        cliente.setEstado(rs.getString(ESTADO));
								        cliente.setFechaRegistro(rs.getDate(FECHAREGISTRO).toString());
								        
								        Distrito distrito = new Distrito();
								        distrito.setCodigo(rs.getInt(CODDISTRITO));
								        distrito.setCodProvincia(rs.getInt(CODPROVINCIA));
								        distrito.setCodRegion(rs.getInt(CODREGION));
								        distrito.setDescripcion(rs.getString(DISTRITO_DESCRIPCION));
								        cliente.setDistrito(distrito);
								        
								        return cliente;
								    }
	    						  }
								);

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_NRODOCUMENTO, cliente.getNroDocumento())
	    								.addValue(PARAM_RAZONSOCIAL, cliente.getRazonSocial())
	    								.addValue(PARAM_ESTADO, cliente.getEstado()); 
        /*
	    Map<String, Object> parametros = new HashMap<String, Object>(2);
	    parametros.put(PARAM_CRITERIO, criterio);
	    parametros.put(PARAM_VALOR, valor);
	    */
	    // Establecer parametros y ejecutar SP
        Map<String,Object> data = llamarSP.execute(parametros);

        // Obteniendo lista generada de ejecutar SP
		@SuppressWarnings("unchecked")
		List<Cliente> listaCliente= (List<Cliente>)data.get(LISTADO);
        
		// Instanciando objeto ListaCliente
		ListaCliente listaClienteResultado = new ListaCliente();
        // Estableciendo resultado al objeto listaClienteResultado
		listaClienteResultado.setLista(listaCliente);
        
        if (logger.isDebugEnabled() && listaClienteResultado != null)
        {
            logger.debug("size=" + listaClienteResultado.size());
        }
        return listaClienteResultado;
	}

	@Override
	public Cliente obtenerCliente(Cliente cliente) {
		logger.info("obtenerCliente");

		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_OBTENER_CLIENTE).
	    						  returningResultSet(LISTADO, new RowMapper<Cliente>(){
									@Override
									public Cliente mapRow(final ResultSet rs, final int numRow) throws SQLException
								    {
								        final Cliente cliente = new Cliente();
								        cliente.setCodCliente(rs.getInt(CODCLIENTE));
								        cliente.setRazonSocial(rs.getString(RAZONSOCIAL));
								        cliente.setRuc(rs.getString(RUC));
								        cliente.setDireccion(rs.getString(DIRECCION));
								        cliente.setTipoDocumento(rs.getString(TIPODOCUMENTO));
								        cliente.setNroDocumento(rs.getString(NRODOCUMENTO));
								        cliente.setEstado(rs.getString(ESTADO));
								        cliente.setFechaRegistro(rs.getDate(FECHAREGISTRO).toString());
								        
								        Distrito distrito = new Distrito();
								        distrito.setCodigo(rs.getInt(CODDISTRITO));
								        distrito.setCodProvincia(rs.getInt(CODPROVINCIA));
								        distrito.setCodRegion(rs.getInt(CODREGION));
								        distrito.setDescripcion(rs.getString(DISTRITO_DESCRIPCION));
								        cliente.setDistrito(distrito);
								        
								        return cliente;
								    }
	    						  }
								 )
								 .returningResultSet(LISTADO_CONTACTOS, new RowMapper<ContactoCliente>(){
									@Override
									public ContactoCliente mapRow(final ResultSet rs, final int numRow) throws SQLException
								    {
								        final ContactoCliente contactoCliente = new ContactoCliente();
								        contactoCliente.setNombresyApellidos(rs.getString(CONTACTO_NOMBRESYAPELLIDOS));
								        contactoCliente.setEmail(rs.getString(CONTACTO_EMAIL));
								        contactoCliente.setIndenvioemail(rs.getString(CONTACTO_INDENVIOEMAIL));
								        contactoCliente.setTelefono(rs.getString(CONTACTO_TELEFONO));
								        contactoCliente.setRelacion(rs.getString(CONTACTO_RELACION));
								        return contactoCliente;
								    }
	    						  });

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_CODCLIENTE, cliente.getCodCliente()); 
        /*
	    Map<String, Object> parametros = new HashMap<String, Object>(2);
	    parametros.put(PARAM_CRITERIO, criterio);
	    parametros.put(PARAM_VALOR, valor);
	    */
	    // Establecer parametros y ejecutar SP
        Map<String,Object> data = llamarSP.execute(parametros);

        // Obteniendo lista generada de ejecutar SP
 		@SuppressWarnings("unchecked")
 		List<Cliente> listaCliente= (List<Cliente>)data.get(LISTADO);
         
 		// Declarando objeto cliente Obtenido
 		Cliente clienteObtenido = null;
 		
 		if (logger.isDebugEnabled() && listaCliente != null)
        {
            logger.debug("size=" + listaCliente.size());
        }
 		
 		if (listaCliente != null && listaCliente.size()>0){
 			// Obteniendo el primer elemento de la lista de cliente
 			clienteObtenido = listaCliente.get(0);
 		
	        // Obteniendo lista de contactos generada de ejecutar SP
			@SuppressWarnings("unchecked")
			List<ContactoCliente> listaContactoCliente= (List<ContactoCliente>)data.get(LISTADO_CONTACTOS);
	        
			// Instanciando objeto ListaContactoCliente
			ListaContactoCliente listaContactoClienteResultado = new ListaContactoCliente();
	        // Estableciendo resultado al objeto ListaCliente
			listaContactoClienteResultado.setLista(listaContactoCliente);
	        
			// Asignando lista de contactos al objeto cliente creado
			clienteObtenido.setListaContactoCliente(listaContactoClienteResultado);
			
	        if (logger.isDebugEnabled() && listaContactoCliente != null)
	        {
	            logger.debug("sizeContactos=" + listaContactoCliente.size());
	        }
 		}
        return clienteObtenido;
	}
	
	@Override
	public Cliente obtenerClientePorDocumento(Cliente cliente) {
		logger.info("obtenerClientePorDocumento");

		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_OBTENER_CLIENTE_PORDOC).
	    						  returningResultSet(LISTADO, new RowMapper<Cliente>(){
									@Override
									public Cliente mapRow(final ResultSet rs, final int numRow) throws SQLException
								    {
								        final Cliente cliente = new Cliente();
								        cliente.setCodCliente(rs.getInt(CODCLIENTE));
								        cliente.setRazonSocial(rs.getString(RAZONSOCIAL));
								        cliente.setRuc(rs.getString(RUC));
								        cliente.setDireccion(rs.getString(DIRECCION));
								        
								        cliente.setTipoDocumento(rs.getString(TIPODOCUMENTO));
								        cliente.setNroDocumento(rs.getString(NRODOCUMENTO));
								        cliente.setEstado(rs.getString(ESTADO));
								        cliente.setFechaRegistro(rs.getDate(FECHAREGISTRO).toString());
								        
								        Distrito distrito = new Distrito();
								        distrito.setCodigo(rs.getInt(CODDISTRITO));
								        distrito.setCodProvincia(rs.getInt(CODPROVINCIA));
								        distrito.setCodRegion(rs.getInt(CODREGION));
								        distrito.setDescripcion(rs.getString(DISTRITO_DESCRIPCION));
								        cliente.setDistrito(distrito);
								        
								        return cliente;
								    }
	    						  }
								 )
								 .returningResultSet(LISTADO_CONTACTOS, new RowMapper<ContactoCliente>(){
									@Override
									public ContactoCliente mapRow(final ResultSet rs, final int numRow) throws SQLException
								    {
								        final ContactoCliente contactoCliente = new ContactoCliente();
								        contactoCliente.setNombresyApellidos(rs.getString(CONTACTO_NOMBRESYAPELLIDOS));
								        contactoCliente.setEmail(rs.getString(CONTACTO_EMAIL));
								        contactoCliente.setIndenvioemail(rs.getString(CONTACTO_INDENVIOEMAIL));
								        contactoCliente.setTelefono(rs.getString(CONTACTO_TELEFONO));
								        contactoCliente.setRelacion(rs.getString(CONTACTO_RELACION));
								        return contactoCliente;
								    }
	    						  });

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_TIPODOCUMENTO, cliente.getTipoDocumento())
	    								.addValue(PARAM_NRODOCUMENTO, cliente.getNroDocumento()); 

	    // Establecer parametros y ejecutar SP
        Map<String,Object> data = llamarSP.execute(parametros);

        // Obteniendo lista generada de ejecutar SP
 		@SuppressWarnings("unchecked")
 		List<Cliente> listaCliente= (List<Cliente>)data.get(LISTADO);
         
 		// Declarando objeto cliente Obtenido
 		Cliente clienteObtenido = null;
 		
 		if (logger.isDebugEnabled() && listaCliente != null)
        {
            logger.debug("size=" + listaCliente.size());
        }
 		
 		if (listaCliente != null && listaCliente.size()>0){
 			// Obteniendo el primer elemento de la lista de cliente
 			clienteObtenido = listaCliente.get(0);
 		
	        // Obteniendo lista de contactos generada de ejecutar SP
			@SuppressWarnings("unchecked")
			List<ContactoCliente> listaContactoCliente= (List<ContactoCliente>)data.get(LISTADO_CONTACTOS);
	        
			// Instanciando objeto ListaContactoCliente
			ListaContactoCliente listaContactoClienteResultado = new ListaContactoCliente();
	        // Estableciendo resultado al objeto ListaCliente
			listaContactoClienteResultado.setLista(listaContactoCliente);
	        
			// Asignando lista de contactos al objeto cliente creado
			clienteObtenido.setListaContactoCliente(listaContactoClienteResultado);
			
	        if (logger.isDebugEnabled() && listaContactoCliente != null)
	        {
	            logger.debug("sizeContactos=" + listaContactoCliente.size());
	        }
 		}
        return clienteObtenido;
	}

	@Override
	public boolean registrarCliente(Cliente cliente) {
		logger.info("registrarCliente");
		
		int resultado = 0;
		
		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
	    							  .withProcedureName(SP_REGISTRAR_CLIENTE);

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_RAZONSOCIAL, cliente.getRazonSocial())
	    								.addValue(PARAM_RUC, cliente.getRuc())
	    								.addValue(PARAM_DIRECCION, cliente.getDireccion())
	    								.addValue(PARAM_CODDISTRITO, cliente.getDistrito().getCodigo())
	    								.addValue(PARAM_CODPROVINCIA, cliente.getDistrito().getCodProvincia())
	    								.addValue(PARAM_CODREGION, cliente.getDistrito().getCodRegion())
	    								.addValue(PARAM_TIPODOCUMENTO, cliente.getTipoDocumento())
	    								.addValue(PARAM_NRODOCUMENTO, cliente.getNroDocumento())
	    								.addValue(PARAM_ESTADO, cliente.getEstado());
	    
	    // Establecer parametros y ejecutar SP
	    Map<String,Object> data = llamarSP.execute(parametros);
	    
	    // Obteniendo el total de filas afectadas al ejecutar SP
	    resultado += (Integer) data.get(TOTAL_FILAS_AFECTADAS);
	    
	    logger.debug("codigo generado:" + data.get(PARAM_CODCLIENTE));

	    // Obteniendo codigo de cliente
	    int codigoCliente = (Integer)data.get(PARAM_CODCLIENTE);

	    // Obteniendo lista de contactos a agregar
	    ListaContactoCliente listaContactoCliente = cliente.getListaContactoCliente();
	    
	    // Si existen contactos entonces registrarlos
	    if(listaContactoCliente.getLista() != null){
		    // Instanciando variable para llamado de SP
		    SimpleJdbcCall llamarSPContactos = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_REGISTRAR_CONTACTO_CLIENTE);
	
		    for (ContactoCliente contactoCliente : listaContactoCliente.getLista()){
			    // Estableciendo parametros de entrada al SP
			    SqlParameterSource parametrosContactos = new MapSqlParameterSource()
						    								.addValue(PARAM_CODCLIENTE, codigoCliente)
						    								.addValue(PARAM_CONTACTO_NOMBRESYAPELLIDOS, contactoCliente.getNombresyApellidos())
						    								.addValue(PARAM_CONTACTO_EMAIL, contactoCliente.getEmail())
						    								.addValue(PARAM_CONTACTO_INDENVIOEMAIL,contactoCliente.getIndenvioemail())
						    								.addValue(PARAM_CONTACTO_TELEFONO , contactoCliente.getTelefono())
						    								.addValue(PARAM_CONTACTO_RELACION, contactoCliente.getRelacion());
			    
			    // Establecer parametros y ejecutar SP
			    Map<String,Object> dataContacto = llamarSPContactos.execute(parametrosContactos);
			    
			    // Obteniendo el total de filas afectadas al ejecutar SP
			    resultado += (Integer) dataContacto.get(TOTAL_FILAS_AFECTADAS);
		    }
	    }
	    
	    return resultado > 0;
	}

	@Override
	public boolean modificarCliente(Cliente cliente) {
		logger.info("modificarCliente");
		
		int resultado = 0;
		
		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_MODIFICAR_CLIENTE);

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_CODCLIENTE, cliente.getCodCliente())
	    								.addValue(PARAM_RAZONSOCIAL, cliente.getRazonSocial())
	    								.addValue(PARAM_RUC, cliente.getRuc())
	    								.addValue(PARAM_DIRECCION, cliente.getDireccion())
	    								.addValue(PARAM_CODDISTRITO, cliente.getDistrito().getCodigo())
	    								.addValue(PARAM_CODPROVINCIA, cliente.getDistrito().getCodProvincia())
	    								.addValue(PARAM_CODREGION, cliente.getDistrito().getCodRegion())
	    								.addValue(PARAM_TIPODOCUMENTO, cliente.getTipoDocumento())
	    								.addValue(PARAM_NRODOCUMENTO, cliente.getNroDocumento())
	    								.addValue(PARAM_ESTADO, cliente.getEstado());
	    
	    // Establecer parametros y ejecutar SP
	    Map<String,Object> data = llamarSP.execute(parametros);
	    
	    // Obteniendo el total de filas afectadas al ejecutar SP
	    resultado += (Integer) data.get(TOTAL_FILAS_AFECTADAS);
	    
	    // Instanciando variable para llamado de SP Eliminar contactos
	    SimpleJdbcCall llamarSPLimpiarContactos = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_LIMPIAR_CONTACTO_CLIENTE);
	    // Agregar parametro codigo de cliente
	    llamarSPLimpiarContactos.execute(new MapSqlParameterSource()
										     .addValue(PARAM_CODCLIENTE, cliente.getCodCliente()));
	    
	    // Obteniendo lista de contactos a agregar
	    ListaContactoCliente listaContactoCliente = cliente.getListaContactoCliente();
	    
	    // Si existen contactos entonces registrarlos
	    if(listaContactoCliente.getLista() != null){
		    // Instanciando variable para llamado de SP Agregar contactos
		    SimpleJdbcCall llamarSPContactos = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_REGISTRAR_CONTACTO_CLIENTE);
	
		    for (ContactoCliente contactoCliente : listaContactoCliente.getLista()){
			    // Estableciendo parametros de entrada al SP
			    SqlParameterSource parametrosConactos = new MapSqlParameterSource()
						    								.addValue(PARAM_CODCLIENTE, cliente.getCodCliente())
						    								.addValue(PARAM_CONTACTO_NOMBRESYAPELLIDOS, contactoCliente.getNombresyApellidos())
						    								.addValue(PARAM_CONTACTO_EMAIL, contactoCliente.getEmail())
						    								.addValue(PARAM_CONTACTO_INDENVIOEMAIL,contactoCliente.getIndenvioemail())
						    								.addValue(PARAM_CONTACTO_TELEFONO , contactoCliente.getTelefono())
						    								.addValue(PARAM_CONTACTO_RELACION, contactoCliente.getRelacion());
			    
			    // Establecer parametros y ejecutar SP
			    Map<String,Object> dataContacto = llamarSPContactos.execute(parametrosConactos);
			    
			    // Obteniendo el total de filas afectadas al ejecutar SP
			    resultado += (Integer) dataContacto.get(TOTAL_FILAS_AFECTADAS);
		    }
	    }
	    return resultado > 0;
	}

	@Override
	public boolean eliminarCliente(Cliente cliente) {
		logger.info("eliminarCliente");
		
		// Instanciando variable para llamado de SP
	    SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate).withProcedureName(SP_ELIMINAR_CLIENTE);

	    // Estableciendo parametros de entrada al SP
	    SqlParameterSource parametros = new MapSqlParameterSource()
	    								.addValue(PARAM_CODCLIENTE, cliente.getCodCliente());
	    
	    // Establecer parametros y ejecutar SP
	    Map<String,Object> data = llamarSP.execute(parametros);
	    
	    // Obteniendo el total de filas afectadas al ejecutar SP
	    int totalRegistrosAfectado = (Integer) data.get(TOTAL_FILAS_AFECTADAS);
	    
	    return totalRegistrosAfectado > 0;
	}

}
